package sk.tuke.constraintbuilder.views.components;

import java.util.ArrayList;

import javax.xml.bind.JAXBException;

import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.internal.core.BinaryType;
import org.eclipse.jdt.internal.core.SourceType;
import org.eclipse.jdt.ui.IJavaElementSearchConstants;
import org.eclipse.jdt.ui.JavaUI;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.ListViewer;
import org.eclipse.jface.viewers.ViewerDropAdapter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.List;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.SelectionDialog;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ImageHyperlink;

import sk.tuke.constraintbuilder.editor.ConstraintEditorController;
import sk.tuke.constraintbuilder.editor.IConstraintController;
import sk.tuke.constraintbuilder.model.Annotations;
import sk.tuke.constraintbuilder.model.Choice;
import sk.tuke.constraintbuilder.model.ConstraintPatterns.ContentType;
import sk.tuke.constraintbuilder.model.IElementOfStructuredElement;
import sk.tuke.constraintbuilder.model.ILeaf;
import sk.tuke.constraintbuilder.model.Not;
import sk.tuke.constraintbuilder.model.StructuredRuleElement;
import sk.tuke.constraintbuilder.model.Value;
import sk.tuke.constraintbuilder.utils.MyImages;
import sk.tuke.constraintbuilder.utils.MyUtils;

public class StructuredElementRulePanel extends Composite implements IRulePanel {

	private final FormToolkit toolkit = new FormToolkit(Display.getCurrent());
	private StructuredRuleElement element;
	
	private boolean isTemplate;
	private java.util.List<String> valueList = new ArrayList<>();

	//Controller
	private IConstraintController controller;
	
	//Components
	private ListViewer listViewer;
	private Label descriptionLabel;
	private List list;
	private Label multLabel;
	private Composite composite;
	private Composite parent;

	
	public StructuredElementRulePanel(Composite parent, StructuredRuleElement element, IConstraintController controller, boolean isTemplate) {
		super(parent, SWT.BORDER);
		this.parent = parent;
		this.element = element;
		this.controller=controller;
		this.isTemplate = isTemplate;
		addDisposeListener(new DisposeListener() {
			public void widgetDisposed(DisposeEvent e) {
				toolkit.dispose();
			}
		});		
		toolkit.adapt(this);
		toolkit.paintBordersFor(this);
		GridLayout gridLayout = new GridLayout(6, false);
		gridLayout.marginHeight = 3;
		setLayout(gridLayout);
		createControl();
		showRule();
	}
	
	private void createControl(){	
		descriptionLabel = toolkit.createLabel(this, "Description", SWT.NONE);
		descriptionLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
		
		multLabel = new Label(this, SWT.NONE);
		multLabel.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false, 1, 1));
		toolkit.adapt(multLabel, true, true);
		multLabel.setText("New Label");
		
		if(!isTemplate){
			composite = new Composite(this, SWT.NONE);
			composite.setLayout(null);
			composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1));
			toolkit.adapt(composite);
			toolkit.paintBordersFor(composite);
		
			
			listViewer = new ListViewer(composite, SWT.BORDER | SWT.V_SCROLL);
			listViewer.setContentProvider(ArrayContentProvider.getInstance());
			listViewer.setLabelProvider(new LabelProvider() {
				@Override
				public String getText(Object element) {
					String qName = (String) element;
					int i = qName.lastIndexOf('.'); 
					return qName.substring(i+1); 
					
				};
			});
			listViewer.setInput(valueList);
			listViewer.addDoubleClickListener(new IDoubleClickListener() {
				@Override
				public void doubleClick(DoubleClickEvent event) {
					IStructuredSelection selection = (IStructuredSelection) listViewer.getSelection();
			        if (!selection.isEmpty()){
			        	String item = (String) selection.getFirstElement();
						String newItem = valueList.get(valueList.indexOf(item));
						ContentType contentType = ((ConstraintEditorController)controller).getConstraintPatterns().getContentType(element.getName());
						if(contentType==ContentType.ANNOTATION_CONTAINER || contentType==ContentType.TYPE_ELEMENT){
							IRunnableContext context = PlatformUI.getWorkbench().getProgressService();
							IJavaSearchScope scope = SearchEngine.createWorkspaceScope();
							try {
								SelectionDialog dialog = JavaUI.createTypeDialog(getShell(), context, scope, IJavaElementSearchConstants.CONSIDER_ALL_TYPES, false);
								dialog.open();
								Object[] results = dialog.getResult();
								
								if(results[0] instanceof BinaryType){
									newItem = ((BinaryType)results[0]).getFullyQualifiedName();
								}
								if(results[0] instanceof SourceType){
									newItem = ((SourceType)results[0]).getFullyQualifiedName();
								}
							} catch (JavaModelException exc) {
								System.out.println("<Open Type Dialog> JavaModelException");
							}
						}
						else{
							InputDialog dialog = new InputDialog(getShell(), "Change Value", "New Value:", item, null);
							dialog.open();
							newItem = dialog.getValue();
						}
						if(newItem!=null){
							valueList.remove(item);
							valueList.add(newItem);
							listViewer.refresh();
							adaptList();
						}
			        }
				}
			});
			int operations = DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_DEFAULT;
			Transfer[] transferTypes = new Transfer[]{TextTransfer.getInstance()};
			ViewerDropAdapter dropListener = new ViewerDropAdapter(listViewer) {
				@Override
				public boolean performDrop(Object data) {		
					if(data instanceof String){						
		    			IElementOfStructuredElement subElement = element.getElement();
		    			if(subElement!=null){
		    				if(subElement instanceof Value){
		    					valueList.clear();
		    				}
		    				else if(valueList.contains("<empty>")){
		    					valueList.clear();
		    				}
		    			}
						valueList.add((String) data);
						listViewer.refresh();
						adaptList();
						return true;
					}
					else return false;
				}
				@Override
				public boolean validateDrop(Object target, int operation,TransferData transferType) {
					return true;
				}
			};
			listViewer.addDropSupport(operations, transferTypes, dropListener);
			
			ImageHyperlink minusLink = toolkit.createImageHyperlink(this, SWT.NONE);
			toolkit.paintBordersFor(minusLink);
			minusLink.setImage(MyImages.MINUS);
			minusLink.setToolTipText("Remove selected item");
			minusLink.addMouseListener(new MouseAdapter() {
				public void mouseDown(MouseEvent e) {
					if(valueList.size()>1){
						IStructuredSelection selection = (IStructuredSelection) listViewer.getSelection();
				        if (!selection.isEmpty()){
							String item = (String) selection.getFirstElement();
							valueList.remove(item);
							listViewer.refresh();
							adaptList();
				        }
					}
				}
			});
			
			ContentType contentType = ((ConstraintEditorController)controller).getConstraintPatterns().getContentType(element.getName());
			if(contentType==ContentType.ANNOTATION_CONTAINER){
				ImageHyperlink detailsLink = toolkit.createImageHyperlink(this, SWT.NONE);
				toolkit.paintBordersFor(detailsLink);
				detailsLink.setImage(MyImages.DETAILS);
				detailsLink.setToolTipText("Details");
				detailsLink.addMouseListener(new MouseAdapter() {
					public void mouseDown(MouseEvent e) {
						new AnnotationContainerDialog(getShell(), element).open();
					}
				});
			}
		}

		ImageHyperlink removeLink = toolkit.createImageHyperlink(this, SWT.NONE);
		toolkit.paintBordersFor(removeLink);
		removeLink.setImage(MyImages.REMOVE);
		removeLink.setToolTipText("Remove rule");
		removeLink.addMouseListener(new MouseAdapter() {
			public void mouseDown(MouseEvent e) {
				controller.removeRuleElement(element);
				dispose();
				adaptParents();
			}
		});
	}
	
	public void showRule(){
		String description;
		try {
			description = MyUtils.getConstraintPatterns().getDescription(element.getName());
		} catch (JAXBException e1) {
			description = element.getName();
		}
		if(description!=null)descriptionLabel.setText(description);
		IElementOfStructuredElement subElement = element.getElement();
		if(isTemplate){
			multLabel.setText("");
			if(subElement instanceof Not)multLabel.setText("| Not (Negative rule)");
			if(subElement instanceof Choice)multLabel.setText("| Choice ");
			toolkit.adapt(this);
			adaptParents();
		}else{
			valueList.clear();
			if(subElement instanceof Value){
				multLabel.setText(":");
				String value = ((Value)subElement).getValue();
				if(value!=null){
					valueList.add(value);
				}
			}
			if(subElement instanceof Annotations){
				multLabel.setText("| Annotations :");
				java.util.List<ILeaf> leaves = ((Annotations)subElement).getElements();
				if(leaves != null){
					for (ILeaf leaf : leaves) {
						valueList.add(leaf.toString());
					}
				}
			}
			if(subElement instanceof Not){
				multLabel.setText("| Not :");
				java.util.List<ILeaf> leaves = ((Not)subElement).getElements();
				if(leaves != null){
					for (ILeaf leaf : leaves) {
						valueList.add(leaf.toString());
					}
				}
			}
			if(subElement instanceof Choice){
				multLabel.setText("| Choice:");
				java.util.List<ILeaf> leaves = ((Choice)subElement).getElements();
				if(leaves != null){
					for (ILeaf leaf : leaves) {
						valueList.add(leaf.toString());
					}
				}
			}
			if(valueList.size()==0){
				valueList.add("<empty>");
			}
			listViewer.refresh();
			adaptList();
		}
	}
	
	public void adaptList(){
		list = listViewer.getList();
		Point size = list.computeSize(SWT.DEFAULT, SWT.DEFAULT);
		list.setBounds(0, 0, size.x, size.y);
		toolkit.adapt(list, true, true);
		this.layout();
		adaptParents();
	}
	
	private void adaptParents(){
		parent.layout();
		parent.getParent().layout();
		parent.getParent().getParent().layout();
	}

	@Override
	public void doSave() {
		if (!this.isDisposed() && !isTemplate) {
			if(controller instanceof ConstraintEditorController){
				String[] array = valueList.toArray(new String[valueList.size()]);
				((ConstraintEditorController) controller).setElementValues(element, array);
			}	
		}
	}
	
	public void dispose(){
		super.dispose();
	}
}
